#布林线外，波动率达到一定水平
from tqsdk import TqApi, TqAuth
from tqsdk.ta import BOLL
from tqsdk.tafunc import crossup,crossdown,every
from time import time,strftime
import csv
from collections import deque
from utils import logger,IsInMarketOpenCloseTime,makeVolPriceList
from user_info import user_info as info 
from thosttraderapi import THOST_FTDC_D_Buy,THOST_FTDC_D_Sell
# tqacc = TqAccount("B宝城期货", "123456", "123456")   # Z中钢期货  8600276
# tqacc = TqAccount("S上海东亚", "600219", "123456") 
# tqacc = TqAccount("S上海东方", "9020659", "czp666666") 
# tqacc = TqAccount("Z中钢期货", "8600276", "czp66666") 
api = TqApi(auth=TqAuth(info['userinfo']['TQ_username'],info['userinfo']['TQ_password']))
# account = tqacc.get_account()
# print(account.balance)
def getSecondNum(time_str):
    time_str=time_str.split(' ')[1].split('.')[0]
    secomdNum=int(time_str.split(':')[2])
    return time_str,secomdNum
#批量订阅  初始化
for k,v in info['instrumenID_dc'].items():
    codeDate=v['exchange']+'.'+k
    v['quote']=api.get_quote(codeDate)
    v['kline']=api.get_kline_serial(codeDate, 60)
    # v['kline_30min']=api.get_kline_serial(codeDate, 60*30)
    #上次触发布林线的时间
    v['lastWarnStamp']=time()  
    #上次触发进仓信号时间
    v['lastSignalStamp']=time()  
    # v['last_second_num']=-1
    v['盘口变化']=deque([0],maxlen=60)
    v['盘口波动率']=0
    v['last_askprice_1']=v['quote'].ask_price1
    v['last_bidprice_1']=v['quote'].bid_price1
    v['lastSecondNum']=0
    v['tradedPriceDeque']=deque()   #最新成交价
    v['RIMdeque']=deque()  #盘口
    v['blowInBottom']=False #现价低于下轨
    v['upOnTop']=False   #现价高于上轨
    v['MaxBodong']={'stamp':time(),'value':40}
# sleep(3)
# signalHandle.OrderSignal.emit({'a':123})
# print('信号发出去了')
def HandleTragyRes(res,k,v,signalHandle):
    if time()-v['lastSignalStamp']<3:return
    quote=v['quote']
    curSecondNum=v['curSecondNum']
    askDIVbidVolumeRate=v['askDIVbidVolumeRate']
    openDic={}
    openDic['InstrumentID']=k
    openDic['direction']=res['direction']
    openDic['tragyName']=res['tragyName']
    openDic['tragyArg']=res['tragyArg']
    openDic['profitCoin']=res['profitCoin']
    openDic['volume']=res['volume'] if 'volume' in res else v['volume']

    aveVolume=v['CurVolume']//v['curSecondNum'] if v['curSecondNum']>0 else v['CurVolume']
    if res['direction']==THOST_FTDC_D_Buy:
        step=-v['miniPrice']
        startPrice= quote.bid_price1+v['miniPrice'] if quote.bid_volume1+quote.ask_volume1>aveVolume else quote.bid_price1
    else:#卖
        step=v['miniPrice']
        startPrice= quote.ask_price1-v['miniPrice'] if quote.bid_volume1+quote.ask_volume1>aveVolume else quote.ask_price1
    openDic['VolPriceList']=makeVolPriceList(startPrice,v['volumeTuple'],step)
    logger.info(' '.join([k,'秒数',str(curSecondNum),'报单价','%.2f'%startPrice,openDic['tragyName'],openDic['tragyArg'],'std','%.4f'%v['std'],'波动率：','%d'%v['BoDongCount']]))
    signalHandle.OrderSignal.emit(openDic)
    v['lastSignalStamp']=time()
def TQWaiUpdate(signalHandle):
    bodongDeque=[]
    curSecondNum=0
    flushTime=0
    # t0=time()
    while True:
        #一个循环耗时0.03~0.2秒之间，不超过0.5秒就行
        # print('耗时',time()-t0)
        api.wait_update()
        # t0=time()
        flushTime+=1
        if flushTime > 500:
            print(strftime("%H:%M:%S"))
            flushTime=0

        #开收盘三分钟，不做
        if IsInMarketOpenCloseTime():continue
        for k,v in info['instrumenID_dc'].items():
            quote=v['quote']       
            #有可能某一个品种没有变化
            if not api.is_changing(quote):continue
            v['tradedPriceDeque'].append(quote.last_price)
            v['lastSecondNum']=curSecondNum
            time_str,curSecondNum=getSecondNum(quote.datetime)
            v['curSecondNum']=curSecondNum
            #1分钟换线   记录波动率
            if curSecondNum<v['lastSecondNum']:
                # print('换线')
                tu=(*quote.datetime.split(' '),k,BoDongCount)
                bodongDeque.append(tu)
            #记录波动率 写入CSV文件
            if curSecondNum>10 and len(bodongDeque)>10:
                # print('csv写入')
                with open('bodong.csv', 'a', encoding='utf-8',newline='') as file_obj:
                    CSV_writer = csv.writer(file_obj)
                    CSV_writer.writerows(bodongDeque)
                    bodongDeque.clear()
            
            boll=BOLL(v['kline'], v['boll_N'], v['boll_P'])
            CurVolume=v['kline'].iloc[-1].volume
            v['CurVolume']=CurVolume
            std_ls=boll['std']/v['miniPrice']
            std=std_ls.iloc[-1]
            v['std']=std
            v['std_ls']=std_ls
            v['boll']=boll
            v['maxSTD']=std_ls[-25:].max()  #最近25分钟内最大的STD
            delta=(abs(v['last_askprice_1']-quote.ask_price1)+abs(v['last_bidprice_1']-quote.bid_price1))/v['miniPrice']
            v['盘口变化'].append(delta)
            #过去一分钟的盘口
            v['RIMdeque'].append((quote.bid_price1,quote.ask_price1))
            #更新数据
            v['last_askprice_1']=quote.ask_price1
            v['last_bidprice_1']=quote.bid_price1
            
            # if len(v['盘口变化'])>60:v['盘口变化'].popleft()
            # if len(v['tradedPriceDeque'])>120:v['tradedPriceDeque'].popleft()
            # if len(v['RIMdeque'])>120:v['RIMdeque'].popleft()
            # if len(v['RIMdeque'])>30:
            #     askPriceDelta=(v['RIMdeque'][-1][1]-v['RIMdeque'][-10][1])/v['miniPrice']
            #     bidPriceDelta=(v['RIMdeque'][-1][0]-v['RIMdeque'][-10][0])/v['miniPrice']
            #     v['askPriceDelta']=askPriceDelta
            #     v['bidPriceDelta']=bidPriceDelta
                #卖一价急速变化
                # if askPriceDelta>4:print('急速上涨 ',k,time_str,askPriceDelta,v['RIMdeque'][-1][1])
                # if bidPriceDelta<-5:print('急速下跌 ',k,time_str,bidPriceDelta,v['RIMdeque'][-1][0])
            
            #波动率
            BoDongCount=sum(v['盘口变化']) #if v['last_askprice_1'] > 0 else 0
            #三分钟以内的最大波动率
            if time()-v['MaxBodong']['stamp']>200 or BoDongCount>v['MaxBodong']['value']:
                v['MaxBodong']['value']=BoDongCount
                v['MaxBodong']['stamp']=time()




            #K线高度
            # CurHeight=(quote.last_price-v['kline'].iloc[-1]['open'])/v['miniPrice']
            #从开发研究策略，到策略上实盘之前，
            # 先标记记录   开仓信号
            if std<2.5:continue
            if BoDongCount<20:continue
            v['BoDongCount']=BoDongCount

            v['last_price']=quote.last_price
            if quote.last_price<boll.iloc[-1]['bottom']:
                v['blowInBottom']=True
                if time()-v['lastWarnStamp']>10:
                    ##下穿布林线bottom
                    v['lastWarnStamp']=time()#更新
                    # ValueError: cannot convert float NaN to integer
                    aveVol=int(CurVolume/curSecondNum) if curSecondNum>0 else CurVolume
                    msg='%s last_price: %.1f 下轨: %.1f STD:%.2f 波动率：%d 平均成交量：%d'%(k,quote.last_price,boll.iloc[-1]['bottom'],std,BoDongCount,aveVol)
                    logger.info(msg)
            elif quote.last_price>boll.iloc[-1]['top']:
                v['upOnTop']=True
                if time()-v['lastWarnStamp']>10:
                    ##上穿布林线top
                    v['lastWarnStamp']=time()#更新
                    aveVol=int(CurVolume/curSecondNum) if curSecondNum>0 else CurVolume
                    msg='%s last_price: %.1f 上轨: %.1f STD:%.2f 波动率：%d 平均成交量：%d'%(k,quote.last_price,boll.iloc[-1]['top'],std,BoDongCount,aveVol)
                    logger.info(msg)
            else:
                v['blowInBottom']=False
                v['upOnTop']=False
            #卖一量除以买一量
            v['askDIVbidVolumeRate']=quote.ask_volume1 / quote.bid_volume1 if quote.bid_volume1>0 else 1
            #一开始做好一个品种，挣钱了再加品种
            #品种与品种之间不干扰，策略与策略之间不干扰
            #时间，位置，形态，三个维度，定位反转点
            for tragy in v['conditionList']:
                if tragy['condition'](v):
                    HandleTragyRes(tragy['res'],k,v,signalHandle)
                    break
            